package com.mtw.bbs.common.web.handler;



import com.mtw.bbs.common.core.enums.ResultCode;
import com.mtw.bbs.common.core.exception.AuthException;
import com.mtw.bbs.common.core.exception.BusinessException;
import com.mtw.bbs.common.core.security.UserDetails;
import com.mtw.bbs.common.core.security.context.SecurityContextHolder;
import com.mtw.bbs.common.core.vo.Result;
import feign.FeignException;
import feign.codec.DecodeException;
import io.micrometer.core.instrument.config.validate.ValidationException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.net.BindException;
import java.sql.SQLException;
import java.text.ParseException;


/**
 * 全局异常处理
 */
@Slf4j
@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE) // 优先级
@ResponseStatus(code = HttpStatus.BAD_REQUEST) // 统一 HTTP 状态码
public class GlobalExceptionHandler {



    /**
     * 拦截 FeignException 异常，Jackson 处理失败等情况会进入
     */
    @ExceptionHandler(FeignException.class)
    public Result<?> handleFeignException(FeignException e) {
        log.error("FeignException: ", e);
        return Result.fail(ResultCode.SERVICE_EXP.getCode(), e.getMessage());
    }



    /**
     * 拦截 DecodeException 异常，decoder 中抛出的自定义全局异常会进入此处
     */
    @ExceptionHandler(DecodeException.class)
    public Result<?> handleDecodeException(DecodeException e) {
        Throwable cause = e.getCause();
        if (cause instanceof BusinessException be) {
            // 上游符合全局响应包装约定的再次抛出即可
            return Result.fail(be.getCode(), be.getMessage());
        }
        log.error("DecodeException: ", e);
        return Result.fail(ResultCode.SERVICE_EXP.getCode(), e.getMessage());
    }


    /**
     * 400参数错误异常
     */
    @ResponseBody
    @ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class})
    public Result handleParameterVerificationException(@NonNull Exception e) {

        String userId = this.getUser();

        if (e instanceof BindException) {
            log.error("用户id：{}。BindException：",userId, e);
        }  else if (e instanceof ValidationException){
            log.error("用户id：{}。ValidationException：",userId, e);
        }else {
            log.error("用户id：{}。MethodArgumentNotValidException：",userId, e);
        }
        return Result.fail(ResultCode.SERVICE_EXP);
    }

    /**
     * 自定义权限异常处理
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler({AuthException.class})
    public Result authExceptionHandler(AuthException e) {

        String userId = this.getUser();
        log.error("用户id：{}。AuthException：",userId, e);
        return Result.fail(ResultCode.AUTH_ERROR.getCode(), e.getMessage());
    }


    /**
     * 自定义业务异常处理
     */
    @ResponseBody
    @ExceptionHandler({BusinessException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result serviceExceptionHandler(BusinessException e) {

        String userId = this.getUser();
        log.error("用户id：{}。BusinessException：",userId, e);
        return Result.fail(ResultCode.SERVICE_EXP.getCode(), e.getMessage());
    }


    /**
     * 处理空指针的异常
     */
    @ResponseBody
    @ExceptionHandler(value = NullPointerException.class)
    public Result exceptionHandler(NullPointerException e) {

        String userId = this.getUser();
        log.error("用户id：{}。NullPointerException：", userId,e);
        return Result.fail(ResultCode.SERVICE_EXP);
    }


    /**
     * sql异常
     */
    @ResponseBody
    @ExceptionHandler(value = SQLException.class)
    public Result exceptionHandler(SQLException e) {

        String userId = this.getUser();
        log.error("用户id：{}。SQLException：",userId, e);
        return Result.fail(ResultCode.SERVICE_EXP);
    }


    /**
     * 格式转换异常
     */
    @ExceptionHandler(value = ParseException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result exceptionHandler(ParseException e) {

        String userId = this.getUser();
        log.error("用户id：{}。ParseException：",userId, e);
        return Result.fail(ResultCode.SERVICE_EXP);
    }



    /**
     * 数学运算异常
     */
    @ExceptionHandler(value = ArithmeticException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result exceptionHandler(ArithmeticException e) {

        String userId = this.getUser();
        log.error("用户id：{}。ArithmeticException：",userId, e);
        return Result.fail(ResultCode.SERVICE_EXP);
    }



    /**
     * 请求拒绝异常
     *
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.NOT_ACCEPTABLE)
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    public Result methodNotSupportedHandler(HttpRequestMethodNotSupportedException e) {

        String userId = this.getUser();
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        log.error("HttpRequestMethodNotSupportedException：url：{}，用户id：{}。",request.getRequestURI(),userId, e);
        return Result.fail(ResultCode.SERVICE_EXP);
    }



    /**
     * 其他异常
     */
    @ResponseBody
    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result processException(RuntimeException e) {

        String userId = this.getUser();
        log.error("未知异常！用户id：{}。",userId, e);
        return Result.fail(ResultCode.SERVICE_EXP);
    }

    /**
     * 获取用户信息
     */
    private String getUser(){
        UserDetails user = SecurityContextHolder.getContext().getAuthentication();
        if (user != null){
            return user.getId();
        }else {
            return null;
        }
    }
}